/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.yonsei.iwb.sdt.algorithm;

import org.yonsei.iwb.sdt.SDTLearningType;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.NodeIterator;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.ResourceFactory;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS;
import com.hp.hpl.jena.vocabulary.XSD;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.yonsei.iwb.sdt.datastructure.DecomposeCandidate;
import org.yonsei.iwb.sdt.datastructure.DecomposeCandidateSet;
import org.yonsei.iwb.sdt.refinement.Refinement;
import org.yonsei.iwb.sdt.ontology.ClassModel;
import org.yonsei.iwb.sdt.ontology.PropertyArc;
import org.yonsei.iwb.sdt.ontology.UncountedResources;
import org.yonsei.iwb.sdt.ontology.UnitClassModel;
import org.yonsei.iwb.sdt.refinement.RefinementType;
import org.yonsei.iwb.sdt.refinement.generator.CardinalityRestrictionRefinementMaker;
import org.yonsei.iwb.sdt.refinement.generator.ConceptConstructorRefinementMaker;
import org.yonsei.iwb.sdt.refinement.generator.ConjunctionRefinementMaker;
import org.yonsei.iwb.sdt.refinement.generator.DisjunctionRefinementMaker;
import org.yonsei.iwb.sdt.refinement.generator.DomainRestrictionRefinementMaker;
import org.yonsei.iwb.sdt.refinement.generator.QualificationRefinementMaker;
import org.yonsei.iwb.sdt.util.UtilFunctions;

/**
 *
 * @author JDK
 */
public class DCSSearcher {

//    Vector refinementSet;
    private Model model;
    private ClassModel classmodel;
    private int learningOption;
    public static int FLAGNUM = 0;
//    private Vector<Resource> instanceSet;
    private ConceptConstructorRefinementMaker ccr;
    private CardinalityRestrictionRefinementMaker crr;
    private QualificationRefinementMaker qrm;
    private ConjunctionRefinementMaker cr;
    private DisjunctionRefinementMaker dr;
    private DomainRestrictionRefinementMaker drr;

    public DCSSearcher(Model model, ClassModel classmodel, int learningOption) {
        this.model = model;
        this.classmodel = classmodel;
//        this.instanceSet = instanceSet;
//        this.ccr = new ConceptConstructorRefinementMaker(this.model);
//        this.crr = new CardinalityRestrictionRefinementMaker(this.model);
        this.learningOption = learningOption;
//        refinementSet = refinementset;
    }

    /**
     *
     * @param dcs SDT 분류를 위한 후보 집합
     * @param targetclass 현재 탐색의 중심이 되는 클래스
     * @param instSet targetClass를 type으로 갖는 인스턴스의 집합
     * @param hisPAset centerClass 확장되는데 활용한 프로퍼티 집합
     * @return
     */
//    public DecomposeCandidateSet searchDecomposeCandidateSet(DecomposeCandidateSet dcs, Resource targetclass, Vector<Resource> instSet, Vector<PropertyArc> hisPAset) {
    public DecomposeCandidateSet searchDecomposeCandidateSet(DecomposeCandidateSet dcs, Vector<Resource> instSet, Vector<PropertyArc> hisPAset) {

        dcs.setPropertySet(hisPAset);

        //find concept constructor refinement based decompose candidate
        if (dcs.getLastAddedTargetClass() == null) {
            this.getAtomicConceptBasedRefinement(dcs, instSet);
        } else {
            Resource targetclass = dcs.getLastAddedTargetClass();
            if (!UncountedResources.SET.contains(targetclass)) {
                if (hisPAset == null) {
                    this.getAtomicConceptApplBasedRefinement(dcs, targetclass, instSet);
                }
                UnitClassModel ucm = getUnitClassModel(targetclass);
                Vector<PropertyArc> presentPAset = ucm.getObjectPropertySet();
                this.getAtomicRoleBasedRefinement(dcs, instSet, presentPAset, hisPAset);
            }
        }

//        Vector<Resource> dtpset = ucm.getDatatypePropertySet();

        while (true) {
            try {
                Thread.sleep(500);
                if (complete()) {
                    break;
                }
            } catch (InterruptedException ex) {
            }
        }
//        dcs.printDecomposeCandidateSet();
        return dcs;
    }
    

    private boolean complete() {
        if (DCSSearcher.FLAGNUM == 0) {
//            System.out.println("#" + DCSSearcher.FLAGNUM);
            return true;
        }
//        System.out.println("#" + DCSSearcher.FLAGNUM);
        return false;
    }

    private UnitClassModel getUnitClassModel(Resource targetclass) {
        if (this.classmodel.containsKey(targetclass)) {
            return this.classmodel.get(targetclass);
        } else {
            UnitClassModel ucm = new UnitClassModel(targetclass, this.model);
            this.classmodel.put(targetclass, ucm);
            return ucm;
        }

    }

    private void getAtomicConceptBasedRefinement(DecomposeCandidateSet dcs, Vector<Resource> instSet) {
        Vector<Resource> classset = this.getClassOfInstance(instSet);
        ConceptConstructorRefinementMaker ccr = new ConceptConstructorRefinementMaker(dcs, this.model, instSet, classset);
        ccr.decomposeCandidateOfCCR();
    }

    private void getAtomicConceptApplBasedRefinement(DecomposeCandidateSet dcs, Resource targetclass, Vector<Resource> instSet) {

        ConceptConstructorRefinementMaker ccr = new ConceptConstructorRefinementMaker(this.model, instSet);

        ResIterator res = this.model.listResourcesWithProperty(RDFS.subClassOf, targetclass);
        List<Resource> classSet = res.toList();

        if (this.checkAllDisjoint(classSet) && !classSet.isEmpty()) {
            Vector<Refinement> refset = ccr.refinementGenerator(classSet);
            if (refset.size() > 1) {
                dcs.add(new DecomposeCandidate(refset, RefinementType.CONCEPTCONSTRUCTOR));
            }
        } else {
            for (int j = 0; j < classSet.size(); j++) {
                Resource r = classSet.get(j);
                Vector<Refinement> refset = ccr.refinementGenerator(r);
                if (refset != null) {
                    dcs.add(new DecomposeCandidate(refset, RefinementType.CONCEPTCONSTRUCTOR));
                }
            }

            if (learningOption == SDTLearningType.USE_CONJUNCTION_AND_DISJUNCTION) {
                for (int k = 0; k < classSet.size() - 1; k++) {
                    for (int n = 1; n < classSet.size(); n++) {
                        if (this.checkDisjointANDsubclass(classSet.get(k), classSet.get(n))) {
                            DCSSearcher.FLAGNUM++;
                            new ConjunctionRefinementMaker(this.model, classSet.get(k), classSet.get(n), instSet, dcs).start();

                            DCSSearcher.FLAGNUM++;
                            new DisjunctionRefinementMaker(this.model, classSet.get(k), classSet.get(n), instSet, dcs).start();

                        }
                    }
                }
            }
        }
    }

    private Vector<Resource> getClassOfInstance(Vector<Resource> instanceSet) {
        Vector<Resource> classes = new Vector();
        for (int i = 0; i < instanceSet.size(); i++) {
            Resource instance = instanceSet.get(i);
            NodeIterator ni = this.model.listObjectsOfProperty(instance, RDF.type);
            while (ni.hasNext()) {
                Resource typeclass = ni.nextNode().asResource();
                if (typeclass.isURIResource()) {
                    if (!classes.contains(typeclass)) {
                        classes.add(typeclass);
                    }
                }
            }
        }
        return classes;
    }

    private void getAtomicRoleBasedRefinement(DecomposeCandidateSet dcs, Vector<Resource> instSet, Vector<PropertyArc> presentPAset, Vector<PropertyArc> hisPAset) {
        ObjectProperty:
        for (int i = 0; i < presentPAset.size(); i++) {
            PropertyArc pa = presentPAset.get(i);
            Property property = pa.getProperty();

            if (!hisPAset.isEmpty()) {
                PropertyArc lastHistoryArc = hisPAset.lastElement();
                if (lastHistoryArc.getProperty().equals(property)) {
                    if (pa.getDirection() != lastHistoryArc.getDirection()) {
                        continue ObjectProperty;
                    }
                }
            }

            // Cardinality Restriction Refinement                                
            Vector<Integer> stvSet = this.getStandardValueOfObjectProperty(pa);
            for (int k = 0; k < stvSet.size(); k++) {
                DCSSearcher.FLAGNUM++;
                new CardinalityRestrictionRefinementMaker(this.model, pa, stvSet.get(k), hisPAset, instSet, dcs).start();
            }

            //Qualification Refinement
            Vector<Resource> connClassSet = this.getConnectedClasses(pa);
            for (int n = 0; n < connClassSet.size(); n++) {
                DCSSearcher.FLAGNUM++;
                new QualificationRefinementMaker(this.model, pa, connClassSet.get(n), hisPAset, instSet, dcs).start();
            }

//            System.out.println(" ---------------- " + learningOption);
            if (learningOption == SDTLearningType.USE_CONJUNCTION_AND_DISJUNCTION) {
//                System.out.println(pa.toString() + "????????????????????????????????????" + connClassSet.size());
                for (int k = 0; k < connClassSet.size() - 1; k++) {
                    for (int b = k + 1; b < connClassSet.size(); b++) {
//                        System.out.println(connClassSet.get(k).getLocalName() + "  /    " + connClassSet.get(b).getLocalName());
                        if (!this.checkDisjointANDsubclass(connClassSet.get(k), connClassSet.get(b))) {
                            DCSSearcher.FLAGNUM++;
                            new ConjunctionRefinementMaker(this.model, connClassSet.get(k), connClassSet.get(b), pa, hisPAset, instSet, dcs).start();
                            DCSSearcher.FLAGNUM++;
                            new DisjunctionRefinementMaker(this.model, connClassSet.get(k), connClassSet.get(b), pa, hisPAset, instSet, dcs).start();
                        }
                    }
                }
            }
        }
    }

    private void getDatatypeRoleBasedRefinement(DecomposeCandidateSet dcs, Vector<Resource> instSet, Vector<Resource> dtpset, Vector<PropertyArc> hisPAset) {
        this.drr = new DomainRestrictionRefinementMaker(this.model, instSet);
        for (int k = 100; k < dtpset.size(); k++) {

            Resource property = dtpset.get(k);

            // Domain Restriction Refinement
            Property prop = this.model.getProperty(property.toString());

            Hashtable<Resource, Vector<RDFNode>> standardResult = this.getStandardValueOfDatatypeProperty(prop);
            if (standardResult != null) {
                Enumeration<Resource> e = standardResult.keys();
                while (e.hasMoreElements()) {
                    Resource datatype = e.nextElement();
                    Vector<RDFNode> standardvalue = standardResult.get(datatype);

                    if (!hisPAset.isEmpty()) {
                        if (datatype.equals(OWL.DataRange)) {
                            DecomposeCandidateSet tempdcs = drr.refinementGenerator(prop, standardvalue, hisPAset, datatype);
                            if (tempdcs != null) {

                                dcs.addAll(tempdcs);
                            }
                        } else {
                            for (int n = 0; n < standardvalue.size(); n++) {
                                DecomposeCandidateSet tempdcs = drr.refinementGenerator(prop, standardvalue.get(n), hisPAset, datatype);
                                if (tempdcs != null) {
                                    dcs.addAll(tempdcs);
                                }
                            }
                        }

                    } else {
                        if (datatype.equals(OWL.DataRange)) {
                            dcs.add(new DecomposeCandidate(drr.refinementGenerator(prop, standardvalue, datatype), RefinementType.DOMAINRESTRICTION));
                        } else {
                            for (int n = 0; n < standardvalue.size(); n++) {
                                Vector<Refinement> refset = drr.refinementGenerator(prop, standardvalue.get(n), datatype);
                                if (refset != null) {
                                    dcs.add(new DecomposeCandidate(refset, RefinementType.DOMAINRESTRICTION));
                                }
                            }
                        }
                    }
                }
            }
        }

    }

    private Vector<Integer> getStandardValueOfObjectProperty(PropertyArc pa) {
        // 어떻게 정할지 고민을 좀 해봐야 할듯.
        if (SearchedPropertyStandards.contains(pa)) {
            return SearchedPropertyStandards.get(pa);
        } else {

            Vector<Integer> standard = new Vector<Integer>();
            standard.add(0);
            Property property = pa.getProperty();
            StmtIterator iter = this.model.listStatements(null, property, (RDFNode) null);
            int highest = 0;
            if (pa.getDirection()) {
                ResIterator rit = this.model.listResourcesWithProperty(property, null);

                while (rit.hasNext()) {
                    Resource r = rit.nextResource();
                    NodeIterator ni = this.model.listObjectsOfProperty(r, property);
                    List li = ni.toList();
//                    if(!standard.contains(li.size())){
//                        standard.add(li.size());                        
//                    }


                    if (highest < li.size()) {
                        highest = li.size();
                    }
                }
            } else {
                while (iter.hasNext()) {
                    Statement st = iter.nextStatement();
                    Resource r = st.getObject().asResource();
                    ResIterator res = this.model.listResourcesWithProperty(property, r);
                    List li = res.toList();
//                    if(!standard.contains(li.size())){
//                        standard.add(li.size());                        
//                    }


                    if (highest < li.size()) {
                        highest = li.size();
                    }
                }
            }

            int lastStandard = highest / 2;
            if ((highest % 2) == 0) {
                if (!standard.contains(lastStandard)) {
                    standard.add(lastStandard);
                }
            } else {
                lastStandard++;
                if (!standard.contains(lastStandard)) {
                    standard.add(lastStandard);
                }
            }

            SearchedPropertyStandards.put(pa, standard);

            return standard;
        }
    }

    private Hashtable<Resource, Vector<RDFNode>> getStandardValueOfDatatypeProperty(Property property) {

        Hashtable<Resource, Vector<RDFNode>> standardResult = new Hashtable<Resource, Vector<RDFNode>>();
        NodeIterator ni0 = this.model.listObjectsOfProperty(property, RDFS.range);
        NodeIterator ni = this.model.listObjectsOfProperty(property);
        Vector<RDFNode> standard = new Vector<RDFNode>();
        if (ni.hasNext()) {
            standard.addAll(ni.toList());
        } else {
            return null;
        }

        while (ni0.hasNext()) {
            Resource li = ni0.nextNode().asResource();
            StmtIterator st2 = this.model.listStatements(li, RDF.type, OWL.DataRange);

            if (li.equals(XSD.xboolean)) {
//                standard.removeElementAt(1);
                standardResult.put(XSD.xboolean, standard);
            } else if (li.equals(XSD.xint)) {
                standardResult.put(XSD.xint, standard);
            } else if (li.equals(XSD.xstring)) {
                standardResult.put(XSD.xstring, standard);
            } else if (st2.hasNext()) {
                standardResult.put(OWL.DataRange, standard);
            }
        }

        return standardResult;
    }

    public Vector<Resource> getConnectedClasses(PropertyArc pa) {
        Vector<Resource> rangeSet = new Vector<Resource>();
        Property prop = pa.getProperty();

        if (pa.getDirection()) {
            NodeIterator ni = this.model.listObjectsOfProperty(prop, RDFS.range);
            while (ni.hasNext()) {
                Resource r = ni.nextNode().asResource();
                if (UncountedResources.SET.contains(r)) {
                    continue;
                }
//                rangeSet.add(r);
                Vector<Resource> subClassSet = UtilFunctions.getSubClasses(r, this.model);
                for (int i = 0; i < subClassSet.size(); i++) {
                    Resource subclass = subClassSet.get(i);
                    if (!rangeSet.contains(subclass)) {
                        if (!UncountedResources.SET.contains(subclass)) {
                            if(subclass.isURIResource()){                                
                                rangeSet.add(subclass);                                
                            }                            
                        }

                    }
                }
            }
        } else {
            NodeIterator ni = this.model.listObjectsOfProperty(prop, RDFS.domain);
            while (ni.hasNext()) {
                Resource r = ni.nextNode().asResource();
                if (UncountedResources.SET.contains(r)) {
                    continue;
                }
//                rangeSet.add(r);
                Vector<Resource> subClassSet = UtilFunctions.getSubClasses(r, this.model);
                for (int i = 0; i < subClassSet.size(); i++) {
                    Resource subclass = subClassSet.get(i);
                    if (!rangeSet.contains(subclass)) {
                        if (!UncountedResources.SET.contains(subclass)) {
                             if(subclass.isURIResource()){
                                rangeSet.add(subclass);                                
                            }                            
                        }
                    }
                }
            }
        }
        return rangeSet;
    }

    /**
     *
     * @param v 클래스 집합
     * @return v에 포함된 클래스들이 서로 Disjoint 한지 여부
     */
    private boolean checkAllDisjoint(List<Resource> v) {
//        boolean tf = false;

        for (int i = 0; i < v.size() - 1; i++) {
            Resource r1 = v.get(i);
            for (int j = 1; j < v.size(); j++) {
                Resource r2 = v.get(j);
                StmtIterator stmt = this.model.listStatements(r1, OWL.disjointWith, r2);
                if (!stmt.hasNext()) {
                    return false;
                }
            }
        }
        return true;
    }

    private boolean checkDisjointANDsubclass(Resource cen, Resource con) {
//        StmtIterator st1 = this.model.listStatements(cen, RDFS.subClassOf, con);
//        if (st1.hasNext()) {                
//            return true;
//        }
//        StmtIterator st2 = this.model.listStatements(con, RDFS.subClassOf, cen);
//        if (st2.hasNext()) {
//            return true;
//        }
        StmtIterator st = this.model.listStatements(con, OWL.disjointWith, cen);
        if (st.hasNext()) {
            if(!con.equals(cen)){
                return true;                
            }            
        }
        return false;
    }
}
